package common.web.action;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import common.hibernate.service.BaseService;
import common.log.service.LogService;
import common.log.vo.WebLog;
import common.rbac.po.BusinessDataEntity;
import common.rbac.po.IdEntity;
import common.security.web.util.SecurityUtil;
import common.util.HQLUtils;
import common.util.UUIDUtil;
import common.web.vo.Result;

/**
 * @author Administrator
 *
 */
@Controller
public class CommonAction extends MultiActionController {

	private final static String ASC = "asc";
	private final static String DESC = "desc";

	// 开始
	private final static String STARTS_WITH = "2";
	// 结束
	private final static String ENDS_WITH = "4";
	// 精准
	private final static String EXACT = "8";
	// 包含
	private final static String CONTAINS = "16";
	// 大于
	private final static String GREATER_THAN = "32";
	// 大于等于
	private final static String GREATER_THAN_OR_EQUAL = "64";
	// 小于
	private final static String LESS_THAN = "128";
	// 小于等于
	private final static String LESS_THAN_OR_EQUAL = "256";
	// 不等于
	private final static String NOT_EQUAL = "512";
	// 下拉框 精准匹配
	private final static String SELECT = "select";
	private final static String INPUT = "input";

	@Autowired
	@Qualifier("baseServiceImpl")
	protected BaseService baseService;
	@Autowired
	public LogService logService;

	@Autowired
	public Configuration config;

	/**
	 * 记录日志
	 * 
	 * @param log
	 */
	protected void saveLog(WebLog log) {
		logService.saveLog(log);
	}
	
	protected Result list(JSONObject json, Class<?> clazz) {
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		this.handleFilter(clazz, json, criteria);
		this.handleSort(json, criteria);
		return Result.createPageResult(
				baseService.findByCriteriaForPage(criteria, this.getPageNumber(json), this.getPageSize(json)));
	}

	protected void handleFilter(Class<?> clazz, JSONObject json, DetachedCriteria dc) {
		JSONArray filters = new JSONArray();
		getFilter(json, filters);

		if (filters.size() > 0) {
			for (int i = 0; i < filters.size(); i++) {
				JSONObject f = filters.getJSONObject(i);
				handleDetachedCriteria(clazz, dc, f);
			}
		}
	}

	protected String handleFilter(Map<String, Class<?>> clazzMap, JSONObject json, List<Object> params) {
		return handleFilter(clazzMap, json, params, false);
	}
	protected String handleFilter(Map<String, Class<?>> clazzMap, JSONObject json, List<Object> params, boolean isSql) {
		JSONArray filters = new JSONArray();
		getFilter(json, filters);
		StringBuilder hql = new StringBuilder();
		if (filters.size() > 0) {
			for (int i = 0; i < filters.size(); i++) {
				JSONObject f = filters.getJSONObject(i);
				String field = f.getString("field");
				if (StringUtils.contains(field, ".")) {
					String[] split = StringUtils.split(field, ".");
					Map<String, Object> map = handleHqlFiled(clazzMap.get(split[0]), f, split[1], split[0], isSql);
					if (i > 0) {
						hql.append(" and ");
					}
					hql.append(map.get("hql"));
					params.add(map.get("value"));
				}
			}
		}
		if (hql.length() == 0) {
			hql.append(" 1=1 ");
		}
		return hql.toString();
	}
	
	protected String handleFilterForSql(Map<String, Class<?>> clazzMap, JSONObject json, List<Object> params) {
		return handleFilter(clazzMap, json, params, true);
	}

	protected void getFilter(JSONObject json, JSONArray filters) {
		if (null != json.getJSONObject("externalParams")) {
			filters.addAll(json.getJSONObject("externalParams").getJSONArray("filter"));
		}
	}

	protected void handleDetachedCriteria(Class<?> clazz, DetachedCriteria dc, JSONObject f) {
		String type = StringUtils.defaultString(f.getString("type"), EXACT);
		String key = f.getString("field");
		Class<?> ctype = BeanUtils.findPropertyType(key, clazz);
		Object value = f.getObject("term", ctype);
		switch (type) {
		case STARTS_WITH:
			HQLUtils.addLeft(dc, key, value);
			break;
		case ENDS_WITH:
			HQLUtils.addRight(dc, key, value);
			break;
		case EXACT:
			HQLUtils.addEq(dc, key, value);
			break;
		case CONTAINS:
			HQLUtils.addAnywhere(dc, key, value);
			break;
		case GREATER_THAN:
			HQLUtils.addGt(dc, key, value);
			break;
		case GREATER_THAN_OR_EQUAL:
			HQLUtils.addGe(dc, key, value);
			break;
		case LESS_THAN:
			HQLUtils.addLt(dc, key, value);
			break;
		case LESS_THAN_OR_EQUAL:
			HQLUtils.addLe(dc, key, value);
			break;
		case NOT_EQUAL:
			HQLUtils.addNe(dc, key, value);
			break;
		case SELECT:
			HQLUtils.addEq(dc, key, value);
			break;
		case INPUT:
			HQLUtils.addAnywhere(dc, key, value);
			break;
		default:
			break;
		}
	}

	protected Map<String, Object> handleHqlFiled(Class<?> clazz, JSONObject f, String field, String alias, boolean isSql) {
		Map<String, Object> map = new HashMap<>();
		String type = StringUtils.defaultString(f.getString("type"), EXACT);
		Class<?> ctype = BeanUtils.findPropertyType(field, clazz);
		String hqlName = field;
		if (StringUtils.isNotBlank(alias)) {
			hqlName = alias + "." + field;
		}
		Object value = f.getObject("term", ctype);
		boolean isEnum = ctype.isEnum();
		if (isSql && isEnum) {
			Enumerated annotation = null;
			try {
				Field fd = clazz.getField(field);
				annotation = fd.getAnnotation(Enumerated.class);
			} catch (Exception e) {
			}
			if (null == annotation) {
				PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, field);
				Method readMethod = pd.getReadMethod();
				annotation = readMethod.getAnnotation(Enumerated.class);
				if (annotation == null) {
					Method writeMethod = pd.getWriteMethod();
					annotation = writeMethod.getAnnotation(Enumerated.class);
				}
			}
			if (null != annotation && annotation.value().equals(EnumType.ORDINAL)) {
				value = ((Enum<?>) value).ordinal();
			} else {
				value = ((Enum<?>) value).name();
			}
		}
		switch (type) {
		case STARTS_WITH:
			map.put("hql", hqlName + " like ? ");
			map.put("value", "%" + value);
			break;
		case ENDS_WITH:
			map.put("hql", hqlName + " like ? ");
			map.put("value", value + "%");
			break;
		case EXACT:
			map.put("hql", hqlName + " = ? ");
			map.put("value", value);
			break;
		case CONTAINS:
			map.put("hql", hqlName + " like ? ");
			map.put("value", "%" + value + "%");
			break;
		case GREATER_THAN:
			map.put("hql", hqlName + " > ? ");
			map.put("value", value);
			break;
		case GREATER_THAN_OR_EQUAL:
			map.put("hql", hqlName + " >= ? ");
			map.put("value", value);
			break;
		case LESS_THAN:
			map.put("hql", hqlName + " < ? ");
			map.put("value", value);
			break;
		case LESS_THAN_OR_EQUAL:
			map.put("hql", hqlName + " <= ? ");
			map.put("value", value);
			break;
		case NOT_EQUAL:
			map.put("hql", hqlName + " != ? ");
			map.put("value", value);
			break;
		case SELECT:
			map.put("hql", hqlName + " = ? ");
			map.put("value", value);
			break;
		case INPUT:
			map.put("hql", hqlName + " like ? ");
			map.put("value", "%" + value + "%");
			break;
		default:
			break;
		}
		return map;
	}

	protected void handleSort(JSONObject json, DetachedCriteria criteria) {
		JSONArray sort = new JSONArray();
		getSort(json, sort);
		for (Object jss : sort) {
			JSONObject js = (JSONObject) jss;
			if (ASC.equals(js.getString("direction"))) {
				criteria.addOrder(Order.asc(js.getString("field")));
			} else if (DESC.equals(js.getString("direction"))) {
				criteria.addOrder(Order.desc(js.getString("field")));
			}
		}
	}

	protected String handleSort(JSONObject json) {
		JSONArray sort = new JSONArray();
		getSort(json, sort);
		StringBuilder sorts = new StringBuilder();
		if (sort.size() > 0) {
			sorts.append(" order by ");
		}
		for (int i = 0; i < sort.size(); i++) {
			JSONObject js = (JSONObject) sort.get(i);
			if (i > 0) {
				sorts.append(", ");
			}
			sorts.append(js.getString("field"));
			if (ASC.equals(js.getString("direction"))) {
				sorts.append(" asc ");
			} else if (DESC.equals(js.getString("direction"))) {
				sorts.append(" desc ");
			}
		}
		return sorts.toString();
	}

	protected void getSort(JSONObject json, JSONArray sort) {
		if (null != json.getJSONObject("externalParams")) {
			sort.addAll(json.getJSONObject("externalParams").getJSONArray("sort"));
			if (null != sort && sort.size() > 0) {
				Collections.sort(sort, new Comparator<Object>() {
					@Override
					public int compare(Object o1, Object o2) {
						JSONObject j1 = (JSONObject) o1;
						JSONObject j2 = (JSONObject) o2;
						return j1.getIntValue("priority") - j2.getIntValue("priority");
					}

				});
			}
		}
	}

	protected int getPageSize(JSONObject json) {
		try {
			if (null != json.getJSONObject("externalParams")) {
				return json.getJSONObject("externalParams").getIntValue("pageSize");
			}
		} catch (Exception e) {
		}
		return 20;
	}

	protected int getPageNumber(JSONObject json) {
		try {
			if (null != json.getJSONObject("externalParams")) {
				return json.getJSONObject("externalParams").getIntValue("pageNumber");
			}
		} catch (Exception e) {
		}
		return 1;
	}

	public boolean unique(String entityName, String field, Object value, String id) {
		if (StringUtils.isNotBlank(entityName)) {
			DetachedCriteria dc = DetachedCriteria.forEntityName(entityName);
			HQLUtils.addEq(dc, field, value);
			if (StringUtils.isNotBlank(id)) {
				HQLUtils.addNe(dc, "id", id);
			}
			if (this.baseService.findByCriteria(dc).size() == 0) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 设置http不使用缓存
	 * 
	 * @param response
	 */
	public void noCache(HttpServletResponse response) {
		response.setHeader("Cache-Control", "no-cache");
		response.setHeader("Cache-Control", "no-store");
		response.setHeader("Pragma", "no-cache");
		response.setDateHeader("Expires", 0);
	}

	/**
	 * 设置创建、修改 人和时间 如果是需要修改ID则可以新建ID
	 * 
	 * @param obj
	 */
	public void setCommonSaveInfo(Object obj) {
		if (obj instanceof BusinessDataEntity) {
			Date createTime = new Date();
			((BusinessDataEntity) obj).setCreateTime(createTime);
			((BusinessDataEntity) obj).setUpdateTime(createTime);
			String userId = SecurityUtil.getUserName();
			((BusinessDataEntity) obj).setCreator(userId);
			((BusinessDataEntity) obj).setUpdator(userId);
		}
		if (obj instanceof IdEntity) {
			((IdEntity) obj).setId(UUIDUtil.createUuid());
		}
	}

	/**
	 * 设置修改 人和时间
	 * 
	 * @param obj
	 */
	public void setCommonUpdateInfo(Object obj) {
		if (obj instanceof BusinessDataEntity) {
			Date updateTime = new Date();
			((BusinessDataEntity) obj).setUpdateTime(updateTime);
			String userId = SecurityUtil.getUserName();
			((BusinessDataEntity) obj).setUpdator(userId);
		}
	}

}
